/*
 * Copyright 2023-2025, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include <KernelExport.h>

#include <boot/kernel_args.h>
#include <kernel.h>

#include <arch/vm.h>
#include <vm/vm.h>
#include <vm/VMAddressSpace.h>
#include <vm/vm_types.h>

#include "LoongArchVMTranslationMap.h"

// #define TRACE_ARCH_VM
#ifdef TRACE_ARCH_VM
#define TRACE(x...) dprintf(x)
#else
#define TRACE(x...) ;
#endif

status_t
arch_vm_init(kernel_args *args)
{
    TRACE("arch_vm_init\n");
    return B_OK;
}

status_t
arch_vm_init2(kernel_args *args)
{
    TRACE("arch_vm_init2\n");
    return B_OK;
}

status_t
arch_vm_init_post_area(kernel_args *args)
{
    TRACE("arch_vm_init_post_area\n");

    // 创建一个区域覆盖物理内存映射区域
    void *address = (void *)KERNEL_PMAP_BASE;
    area_id area = vm_create_null_area(VMAddressSpace::KernelID(), "physical map area", &address,
                                       B_EXACT_ADDRESS, KERNEL_PMAP_SIZE, 0);

    if (area < 0)
    {
        panic("arch_vm_init_post_area(): 无法创建物理内存映射区域\n");
        return B_ERROR;
    }

    // 创建DMA映射区域
    // 根据龙芯手册第6.2节，DMA映射区域应该位于直接映射区域内
    // 直接映射区域：0x9000000000000000 - 0x907fffffffffffff
    // 我们在此区域内分配一块用于DMA映射
    address = (void *)0x9000000000100000; // 选择一个合适的地址
    size_t dmaSize = 16 * 1024 * 1024;    // 16MB DMA区域

    area = vm_create_null_area(VMAddressSpace::KernelID(), "dma mapping area", &address,
                               B_EXACT_ADDRESS, dmaSize, 0);

    if (area < 0)
    {
        panic("arch_vm_init_post_area(): 无法创建DMA映射区域\n");
        return B_ERROR;
    }

    TRACE("arch_vm_init_post_area: 创建物理内存映射区域: %p, 大小: %#" B_PRIxSIZE "\n",
          (void *)KERNEL_PMAP_BASE, KERNEL_PMAP_SIZE);
    TRACE("arch_vm_init_post_area: 创建DMA映射区域: %p, 大小: %#" B_PRIxSIZE "\n",
          address, dmaSize);

    return B_OK;
}

status_t
arch_vm_init_end(kernel_args *args)
{
    TRACE("arch_vm_init_end(): %" B_PRIu32 " virtual ranges to keep:\n",
          args->arch_args.num_virtual_ranges_to_keep);

    for (int i = 0; i < (int)args->arch_args.num_virtual_ranges_to_keep; i++)
    {
        addr_range &range = args->arch_args.virtual_ranges_to_keep[i];

        TRACE("  start: %p, size: %#" B_PRIxSIZE "\n", (void *)range.start, range.size);

        // 跳过内核地址空间外的范围
        if (!IS_KERNEL_ADDRESS(range.start))
        {
            TRACE("    非内核地址，跳过...\n");
            continue;
        }

        phys_addr_t physicalAddress;
        void *address = (void *)range.start;
        if (vm_get_page_mapping(VMAddressSpace::KernelID(), range.start,
                                &physicalAddress) != B_OK)
            panic("arch_vm_init_end(): 地址 %p 没有页面映射\n", address);

        area_id area = vm_map_physical_memory(VMAddressSpace::KernelID(),
                                              "boot loader reserved area", &address,
                                              B_EXACT_ADDRESS, range.size,
                                              B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
                                              physicalAddress, true);

        if (area < 0)
        {
            panic("arch_vm_init_end(): 无法为引导加载程序保留区域创建区域: %p - %p\n",
                  (void *)range.start, (void *)(range.start + range.size));
        }
    }

    return B_OK;
}

status_t
arch_vm_init_post_modules(kernel_args *args)
{
    TRACE("arch_vm_init_post_modules\n");
    return B_OK;
}

void arch_vm_aspace_swap(struct VMAddressSpace *from, struct VMAddressSpace *to)
{
    LoongArchVMTranslationMap *fromMap = (LoongArchVMTranslationMap *)from->TranslationMap();
    LoongArchVMTranslationMap *toMap = (LoongArchVMTranslationMap *)to->TranslationMap();

    if (fromMap == NULL || toMap == NULL)
        return;

    toMap->ChangeASID(fromMap->ASID());
}